﻿//#define DEBUG_SHOW_CREATION

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using System.Drawing;
using System.Windows.Forms; //for Color class

namespace DirectX_CS
{
	public partial class WaveEditor : Form, IManagedModelTransformable,
		IManagedVertexBuffer, IManagedIndexBuffer, IManagedBuffers, IManagedMaterial,
		IColorable, IAnimated, IRangeModifiable, INamed, IDisposable
	{
		[PGBrowsable]
		public Range3d Range
		{
			get { return new Range3d(XMin, YMin, ZMin, XMax, YMax, ZMax); }
		}
		const float PI = (float)Math.PI;

		protected double mTime = 0;
		protected Device mDevice = null;
		protected bool mRegistered = false;
		protected PrimitiveManager renderer = null; //main memory manager..change name.

		public event VBChangedDelegate VertexBufferChanged;
		
		
		public event IBChangedDelegate IndexBufferChanged;

		protected Material mMaterial;
		protected Color mMaterialColor = Color.Black;
		protected Color mVertexColor = Color.Navy;

		protected int renderCounts = 0;
		private int framesPerRender = 10;
		protected float dTime = .5f;

		private string shortName = "WaveEditor";
		[PGBrowsable]
		public string ShortName
		{
			get { return shortName; }
			set { shortName = value; }
		}
		private static int creationCount = 0;

		private int shortNumber = -1;

		public int ShortNumber
		{
			get { return shortNumber; }
			set { shortNumber = value; }
		}
		[PGBrowsable]
		public string ObjectName
		{
			get { return shortName + " " + shortNumber; }
		}
		[PGBrowsable]
		public int FramesPerRender
		{
			get { return framesPerRender; }
			set { if (value > 1)
				framesPerRender = value; }
		}

		private TransformMatrix modelView = new TransformMatrix();
		[PGBrowsable]
		public TransformMatrix ModelView
		{
			get { return modelView; }
			set { modelView = value; }
		}

		//public delegate double PolarRT_ZFunc(double radius, double theta);

		//protected Array mIndexData = null;
		protected int[] mSurfaceIndices = null;

        protected Array mVertexData = null;


		protected bool initializing = false;

        public WaveEditor(Device device, PrimitiveManager rendManager, DirectXForm formForHandlers)
		{
            Initialize(device, rendManager, formForHandlers);
            Recreate();
        }

		protected DirectXForm mFormForHandlers;

		public void Initialize(Device device, PrimitiveManager rendManager, DirectXForm formForHandlers)
        {
            InitializeComponent();
			this.shortNumber = creationCount++;

			mFormForHandlers = formForHandlers;
			mFormForHandlers.MouseDown += new MouseEventHandler(mFormForHandlers_MouseDown);

            InitializeNUDValues();

            InitializeMaterial();
            renderer = rendManager;
            mDevice = device; //set before registering for sure...

            //InitializeParticles();
            mWaves = new List<EquilibriumWave3d>();
			mWaveListItems = new List<ObjectListItem>();

            //AddRandomWavesSameWay();
            Register();
            pgSelfViewer.SelectedObject = this;

            pgSelfViewer.BrowsableAttributes = new System.ComponentModel.AttributeCollection( new PGBrowsableAttribute());


		}

        public void InitializeNUDValues()
        {
            //float x = xMin + (xMax - xMin) / 2, y = 0, z = zMax;
			//nudAmplitudeMax.Value = 10;
			//nudAmplitudeMin.Value = 5;
			//nudCount.Value = 1;
			//nudLifeTimeMax.Value = 60;
			//nudLifeTimeMin.Value = 50;
            
            decimal xhalf = (decimal)(xMin + (xMax - xMin) / 2);

			nudXMin.Value = (decimal)xMin;
			nudXMax.Value = (decimal)xMax;

			nudYMin.Value = (decimal)yMin;
			nudYMax.Value = (decimal)yMax; 

        }
		public WaveEditor(Device device, PrimitiveManager rendManager, DirectXForm formForHandlers, float xmin, float xmax, int x_count, float ymin, float ymax, int y_count, float zmin, float zmax, bool createData)
		{

			xMin = xmin; xMax = xmax; 
			yMin = ymin; yMax = ymax; 
			zMin = zmin; zMax = zmax;
			xCount = x_count;
			yCount = y_count;
			//zCount = z_count;
			Initialize(device, rendManager, formForHandlers);
			if (createData)
				Recreate();
		}

		public void InitializeParticles()
		{
			int x_count = xCount, y_count = yCount, z_count = zCount; //cache for safe use (no changing while this function is working)
			int ParticleIndex = 0;
			mParticles = new EquilibriumPoint[x_count, y_count, z_count];

			for (int xidx = 0; xidx < x_count; xidx++)
			{
				for (int yidx = 0; yidx < y_count; yidx++)
				{
					for (int zidx = 0; zidx < z_count; zidx++)
					{
						EquilibriumPoint p = new EquilibriumPoint(new Vector3((float)xidx / (float)x_count * (xMax - xMin) + xMin, (float)yidx / (float)y_count * (yMax - yMin) + yMin, (float)zidx / (float)z_count * (zMax - zMin) + zMin));
						p.Index = ParticleIndex;
						mParticles[xidx, yidx, zidx] = p;
						mParticles[xidx, yidx, zidx].Color = MainVertexColor;
						ParticleIndex++;
					}
				}
			}
		}

		public void RemoveAllWaves()
		{
			//mVerticalWaves.Clear(); mHorizontalWaves.Clear(); 
			mWaves.Clear();
		}
	
        [PGBrowsable]
        public double Time
		{
			get { return mTime; }
			set { 
				mTime = value;
				initializing = true;
				nudGlobalTime.Value = (decimal)mTime; 
				nudGlobalTime.Update();
				initializing = false;
			}
		}

        [PGBrowsable]
		public float TimeDelta
		{
			get { return dTime; }
			set
			{
				dTime = value;
				foreach (EquilibriumWave3d w in mWaves)
				{
					w.DeltaTime = value;
				}
			}
		}

		protected EquilibriumPoint[, ,] mParticles = null;

		/*
		one possible idea is too add different types of more complex waves
		//private int[] mIndexData = null;
		List<EquilibriumWave3d> mHorizontalWaves = null;

		[PGBrowsable]
		public List<EquilibriumWave3d> HorizontalWaves
		{
		    get { return mHorizontalWaves; }
		    set { mHorizontalWaves = value; }
		}
		*/
		List<EquilibriumWave3d> mWaves = null;
        [PGBrowsable]
		public List<EquilibriumWave3d> Waves
		{
			get { return mWaves; }
			set { mWaves = value; }
		}

		private List<ObjectListItem> mWaveListItems = null;

		protected List<ObjectListItem> WaveListItems
		{
			get { return mWaveListItems; }
			set { mWaveListItems = value; }
		}
		
		public bool near(float lX, float value)
		{
			return (lX - value) < .1f;
		}
		/// <summary>
		/// 
		/// </summary>
		Buoy onlyBuoy;

		float zMaxDisplacement = 0;
		[PGBrowsable]
		public float ZMaxDisplacement
		{
			get { return zMaxDisplacement; }
		}

		float zMinDisplacement = 0;
		[PGBrowsable]
		public float ZMinDisplacement
		{
			get { return zMinDisplacement; }
		}

		Vector2 desiredBuoyPos = new Vector2(100, 100);


		/// <summary>
		///	 Generates an array with an index associated with point. 
		///	 The points will be used and tranformed to SVectors.
		/// </summary>
		public void GetDisplacementField()
		{
			zMaxDisplacement = 0;
			zMinDisplacement = 0;

			//create buoy if necessary and reset position
			if (onlyBuoy == null)
			{
				onlyBuoy = new Buoy(mDevice, renderer);
				desiredBuoyPos = new Vector2((xMax - xMin) / 2 + xMin, (yMax - yMin) / 2 + yMin);
			}
			//if (onlyBuoy.AssociatedParticle == null)
			onlyBuoy.AssociatedParticle = mParticles[mParticles.GetUpperBound(0)/2, mParticles.GetUpperBound(1)/2, 0]; EquilibriumPoint particle = onlyBuoy.AssociatedParticle;


			int zidx = 0;
			for (int xidx = 0; xidx < mParticles.GetUpperBound(0) + 1; xidx++)
            {
				for (int yidx = 0; yidx < mParticles.GetUpperBound(1) + 1; yidx++)
				{
					//for (zidx = 0; zidx < mParticles.GetUpperBound(2) + 1; zidx++)
					//{
						Vector3 pos3 = mParticles[xidx, yidx, zidx].Position0;
						Vector2 pos2 = new Vector2(pos3.X, pos3.Y);

						Vector3 d = new Vector3(0f, 0f, 0f);
						Vector3 n ;//= new Vector3(rndf(0,1), rndf(0,1), rndf(0,1);
						//n.Normalize();
						SVector dz = new SVector(0, 0, 0);   //color overridden later

						if (mWaves.Count == 0)
						{
							n= new Vector3(rndf(0,1f), rndf(0,1f), rndf(0,1f));
							n.Normalize();
						}
						else
						{
							n = new Vector3(0, 0, 0);
							foreach (EquilibriumWave3d lsw in mWaves)
							{

								dz = lsw.displacement(pos2);
								d = d + dz.Position;
								n = n + lsw.Gradient(pos3); //the normals add due to the linearity of the gradient --- the gradient of a sum (of functions) is the sum of the gradients of functions (see: wikipedia)
							}
						}
						

						//if (d.Length() > 0)
						//	Debug.WriteLine("See this... " + d.Length());

						mParticles[xidx, yidx, zidx].Displacement = d;
						//n.Normalize();
						mParticles[xidx, yidx, zidx].Normal = n;  //set the vertex normal

						if (d.Z > zMaxDisplacement)
							zMaxDisplacement = d.Z;
						if (d.Z < zMinDisplacement)
							zMinDisplacement = d.Z;


						if (mWaves.Count != 0)
							mParticles[xidx, yidx, zidx].Color = Color.Orange;	//color may be overidden


							//set buoy!!!!
							EquilibriumPoint buoyParticle = onlyBuoy.AssociatedParticle;
					
							//after setting displacement the only bouy
							Vector2 buoyXYPos = new Vector2(buoyParticle.Position.X, buoyParticle.Position.Y);
	
							Vector2 dNew2Diff = pos2 - desiredBuoyPos;
							Vector2 dOld2Diff = buoyXYPos - desiredBuoyPos;

							//if (mParticles[xidx, yidx, zidx].Index == onlyBuoy.AssociatedParticle.Index)
							//{
							//    onlyBuoy.Position.Z = 
							//}
							float dNew2DiffLen = dNew2Diff.Length();
							float dOld2DiffLen = dOld2Diff.Length();
							if (dNew2DiffLen < dOld2DiffLen)// && dNew2DiffLen <= abs(xMax - xMin)/xCount)
							{
								particle = mParticles[xidx, yidx, zidx];
							}
							//else if (mParticles[xidx, yidx, zidx].Index == particle.Index)
							//	particle = mParticles[xidx, yidx, zidx];

					//}
				}
            }

			onlyBuoy.AssociatedParticle = particle;
			onlyBuoy.MaxZ = zMaxDisplacement;
				//RemoveDistantWaves();

			//return displacementField;
		}
		

		private void RemoveDistantWaves()
		{
			//now remove waves that arent doing much

			List<EquilibriumWave3d> mLateralWavesToRemove = new List<EquilibriumWave3d>();

			foreach (EquilibriumWave3d lsw in mWaves)
			{
                if (lsw.PastLifeTime((float)Time))
					mLateralWavesToRemove.Add(lsw);
			}
			foreach (EquilibriumWave3d mlwToRemove in mLateralWavesToRemove)
				mWaves.Remove(mlwToRemove);

			Debug.WriteLine("t: " + Time.ToString() + ", wcount:" + mWaves.Count.ToString());

		}


		public static Random rnd = new Random(0);

		public static float rndf(float min, float max)
		{
			int digitsAccuracy = 6;
			float f = 10 ^ digitsAccuracy;
			return rnd.Next((int)(min * f), (int)(max * f))/ (float)f;
		}

		public static decimal f = 10000;

        public static decimal rnddec(decimal min, decimal max)
        {
			if (min >= max)
			{
				decimal temp = min;
				min = max;
				max = temp;
			}
			return rnd.Next((int)(min * f), (int)(max * f)) / (decimal)f;
        }
		
		static int imax(int a, int b)
		{
			return a > b ? a : b;
		}
		static int imin(int a, int b)
		{
			return a < b ? a : b;
		}
		static float min(float a, float b)
		{
			return a < b ? a : b;
		}
		static float max(float a, float b)
		{
			return a > b ? a : b;
		}

		static float abs(float t)
		{
			return ( t >= 0 ? t : -t);
		}
		public Color GetColorForWater()
		{
			return Color.FromArgb(255, MainVertexColor);
		}
	/*
		public Color GetColorFromZ_LighterTop(EquilibriumPoint p)
		{
			float MaxDZ = zMaxDisplacement;
			float MinDZ = zMinDisplacement;
			//if (p.Displacement.LengthSq() > .001)
			//Debug.WriteLine("Stop here: p.Displacement.Length = " + p.Displacement.Length());

			//BEGIN: more blue based on height
			//float zRatio = min(1, abs(p.Position.Z / MaxDZ));
			//int zBase = 100;
			//int zScaled = imin(255, imax(0, (int)((255 - zBase) * (zRatio)) + zBase));
			//Color c = Color.FromArgb(zScaled, 0, (int)(zRatio / 10f * 255f), zScaled);
			//END: more blue based on height

			//whiter the higher
			float dif = abs(MinDZ - MaxDZ);
			float pzRatio;
			if (dif == 0)
			{
				pzRatio = 1;
			}
			else
			{
				pzRatio = abs(MinDZ - p.Displacement.Z) / dif;//max(0, min(1, min(MaxDZ, p.Displacement / MaxDZ))) * 10f / 10f;
			}
			//int whiteThreshold = 10;
			//int WhiteDifference = 255 - whiteThreshold;
			//int z255 = whiteThreshold * (int)pzRatio;
			//if (z255 <= 0)
			//    z255 = 0;
			//else if (z255 > whiteThreshold)
			//    z255 = whiteThreshold;

			Color baseColor = MainVertexColor;
			int a = (int)(245 + 10 * pzRatio );// baseColor.A;
			float colorRatio = (1f - .3f * pzRatio);
			int r = (int)(((float)baseColor.R) * colorRatio);
			int g = (int)(((float)baseColor.G) * colorRatio);
			int b = (int)(((float)baseColor.B) * colorRatio);
	

			Color c = Color.FromArgb(a, r, g, b);
			//Color c = MainVertexColor;
			//Color c = Color.FromArgb(a, rnd, g, b);
			return c;
			//return Color.SlateBlue;
			//get
			//{
			//    //Color sb = Color.SlateBlue;
			//    int zRatio = (int)(255 * Position.Z / MaxZForGradient);
			//    return Color.FromArgb(255, 0,0, zRatio);
			//}
		}
	*/
        /// <summary>
        /// Add some cool waves for stackintg effects that are starting in the same position
        /// </summary>

		//protected List<List<EquilibriumPoint>> idxStrips = null;

		/// <summary>
		/// A triangle
		/// </summary>
		//public class Tri
		//{
		//    public EquilibriumPoint a;	   //here they are in order.. public to all like a struct.
		//    public EquilibriumPoint b;
		//    public EquilibriumPoint c;
		//    public Color SetColors
		//    {
		//        set {
		//        a.Color = value;
		//        b.Color = value;
		//        c.Color = value;
		//        }
		//    }
		//    public Tri(EquilibriumPoint a, EquilibriumPoint b, EquilibriumPoint c)
		//    {
		//        this.a = a; this.b = b; this.c = c;
		//    }
		//}
		/// <summary>
		/// Create the index and vertex data together so we dont split up the functions.
		/// </summary>
		/// 
		//List<Tri> triangles;   //first creation in CreateIndexAndVertexData

		//protected List<Tri> Triangles
		//{
		//    get { return triangles; }
		//    set { triangles = value; }   
		//}
		List<EquilibriumPoint> vertexPoints;

		private List<EquilibriumPoint> VertexPoints
		{
			get { return vertexPoints; }
			set { vertexPoints = value; }
		}

		private void CreateIndexAndVertexData()
		{
			vertexPoints = new List<EquilibriumPoint>();
			GC.Collect();
			
			//int stripVertices = 0;
			int zidx = 0;//zCount - 1;					//use last set of z points for surface... could also use an index in the middle or on bottom.
 	
			int x_count = mParticles.GetUpperBound(0);//length of x axis array
			int y_count = mParticles.GetUpperBound(1);//length of y axis array  
			mSurfaceIndices = new int[x_count * y_count * 6];
			//there are 6 points becuase we are making 2 triangles to make a square and 3 points each needed for each triangle.
			//but 2 of the points share the same vertices
			int surIdx = 0;///Surface index
			int particleCounter = 0;
			for (int xidx = 0; xidx < x_count; xidx++)
			{
				for (int yidx = 0; yidx < y_count; yidx++)
				{		
					/// essentially use 6 indices and 2 triangles and 
					/// 
					//first triangle
					Color mvc = MainVertexColor;
					EquilibriumPoint p1 = mParticles[xidx, yidx, zidx]; p1.Color = mvc;
					p1.Index = particleCounter++; 
					EquilibriumPoint p2 = mParticles[xidx + 1, yidx, zidx]; p2.Color = mvc;
					p2.Index = particleCounter++;
					EquilibriumPoint p3 = mParticles[xidx + 1, yidx + 1, zidx]; p3.Color = mvc;
					p3.Index = particleCounter++;
					//sencond triangle
					EquilibriumPoint p4 = mParticles[xidx, yidx, zidx];	//this is a duplicate point, we will use an index of it instead of putting it in the vertex buffer.
					//..so no need to set color (again) or index
					//p4.Index = particleCounter++;
					EquilibriumPoint p5 = mParticles[xidx, yidx + 1, zidx]; p5.Color = mvc; //try different combinations...
					p5.Index = particleCounter++;
					EquilibriumPoint p6 = mParticles[xidx + 1, yidx + 1, zidx];//p6.Color = mvc ; //this is a duplicate point
					//p6.Index = particleCounter++; //

					//CREATE THE ESSENTIAL TRIANGLES WITH EquilibriumPoint ultimately for our vertex buffer.

					mSurfaceIndices[surIdx++] = p1.Index;
					mSurfaceIndices[surIdx++] = p2.Index;
					mSurfaceIndices[surIdx++] = p3.Index;
	
					//triangles.Add(t1); 

					///create a second set of triangles to fill the gaps...
					///
					vertexPoints.Add(p1); vertexPoints.Add(p2); vertexPoints.Add(p3); vertexPoints.Add(p5);

					//Tri t2 = new Tri(p4, p5, p6); 
					//t2.SetColors(MainVertexColor);
					//triangles.Add(t2);

					//utilize the index buffer so we dont repeat points.

					mSurfaceIndices[surIdx++] = p4.Index;   //duplicate point, thats good so we an reuse the vertex in memory by putting its index in the index buffer.
					mSurfaceIndices[surIdx++] = p5.Index;
					mSurfaceIndices[surIdx++] = p6.Index;	//duplicate point, thats good so we an reuse the vertex in memory.


					///	end create a second set of triangles to fill the gaps...
			#region "CALCULATE OUR INDEX BUFFER"

			//NOW OUR SURFACE INDICES ARE READY TO USE!!
			
			#endregion "CALCULATE OUR INDEX BUFFER"	

				#region "Strips method"
					/*
					if (xidx < x_count - 1)
					{
					
						//old methodology of triangle strip
						if (yidx == 0)
						{
							EquilibriumPoint p1 = mParticles[xidx, yidx, zidx];
							strip.Add(p1);

							EquilibriumPoint p2 = mParticles[xidx + 1, yidx, zidx];
							strip.Add(p2);
							EquilibriumPoint p3 = mParticles[xidx + 1, yidx + 1, zidx];
							strip.Add(p3);
							//aData.Add(p2.GetSVector());

							//aData.Add(p3.GetSVector());
						}
						//old methodology of triangle strip
						else if (yidx < y_count - 1)
						{
							EquilibriumPoint pEtc1 = mParticles[xidx, yidx, zidx];
							strip.Add(pEtc1);
							//aData.Add(pEtc1.GetSVector());
							EquilibriumPoint pEtc2 = mParticles[xidx + 1, yidx + 1, zidx];
							strip.Add(pEtc2);
							//aData.Add(pEtc2.GetSVector());
						}
					 
					}  //idxStrips.Add(strip);		 
				*/
				#endregion

				}	  
			}

			#region "Old strips method"
			///Old methodoly..
			//int idxCount = 0;
			//foreach (List<EquilibriumPoint> strip in idxStrips)
			//{
			//    int stripLength = strip.Count;
			//    idxCount += stripLength;
			//}

			#endregion "Old strips method"   


			SVector[] newVB = new SVector[vertexPoints.Count]; 
			int vbIndex = 0;
			foreach (EquilibriumPoint eq in vertexPoints)
			{
				newVB[vbIndex++] = eq.GetSVector();
			}
			//mVertexData = (from EquilibriumPoint eq in vertexPoints select eq.GetSVector()).ToArray<SVector>();
			mVertexData = newVB;
			
			//mVertexData = GetSVectorsFrom(triangles);//THIS IS OUR VERTEX DATA
			#region "CALCULATE OUR INDEX BUFFER"
			/*
			//this is of course how many vertices a triangle contains multiplied by the number of triangles
			mSurfaceIndices = new int[triangles.Count * 3];  //INITIALIZE size of our INDEX DATA

			int surIdx = 0;	  //the "surface index"
			
			foreach (Tri t in triangles)
			{
				
				//get data from t and use its predefined index.
				mSurfaceIndices[surIdx++] = t.a.Index;
				mSurfaceIndices[surIdx++] = t.b.Index;
				mSurfaceIndices[surIdx++] = t.b.Index;
			}
			//NOW OUR SURFACE INDICES ARE READY TO USE!!
			*/
			#endregion "CALCULATE OUR INDEX BUFFER"		  
		}

	/*   //work on using a vector instead of a list so you can declare the memory up front if efficiency becomes a bigger issue.
	
		 * private SVector[] GetSVectorsFrom(List<Tri> tris)
		{
			//SVector[] list= (from Tri t in tris select t.a, t.b, t.c);
			SVector[] svList = new SVector[tris.Count * 3] ;
			int i = 0;
			foreach (Tri tri in tris)
			{
				
				svList[i] = tri.a.GetSVector(); i++;
				svList[i] = tri.b.GetSVector(); i++;
				svList[i] = tri.c.GetSVector(); i++;
			}
			
			return svList;
		}	
	*/
			  
		/// <summary>
		/// Method to receate requiered buffers.
		/// </summary>
		public void Recreate()
        {

			InitializeParticles();
			GetDisplacementField();
			CreateIndexAndVertexData();
			//if (mEnableCallRenderIndexedPrimitives)
			//    RecreateIndexData();

			VertexBufferChanged(this, mVertexData.Length); //as soon as its registered calls will probably start arriving.
			//if (mEnableCallRenderIndexedPrimitives)
			//IndexBufferChanged(this, mSurfaceIndices.Length); //as soon as its registered calls will probably start arriving.
		}
		public void UpdateTransform()
		{
			VertexBufferChanged(this, mVertexData.Length);
		}
        ~WaveEditor()
		{
			DisposeInstance();
		}

		public void DisposeInstance()
		{
			if (renderer != null)
				renderer.UnregisterPrimitive(this); //cannot hurt to unregister.
			this.Dispose();
		}


        public void Register()
        {
            PrimitiveManager.Register(this); 
			mRegistered = true;
        }

        public void PreRender()
	    {
			if (GlobalTimerEnabled)
			{
				if (renderCounts %  framesPerRender == 0 && mTime != 0)
				{
					Recreate();
				}
				Time += dTime;
				renderCounts++;	
				if (renderCounts % 5 == 0 && Time != 0)
				{
					//RemoveDistantWaves();
					//AddWaves();
					//mLateralWaves.Add(RandomLateralWave0());
					renderCounts = 0; //reset
				}
			}
        }

		public void ResetAnimated()
		{
			Time = 0;
			renderCounts = 0;

			//InitializeParticles();
			RemoveAllWaves();
			//AddRandomWavesSameWay();
		}
		bool drawPoints = false;

		public bool DrawPoints
		{
			get { return drawPoints; }
			set { drawPoints = value; }
		}

        public void RenderPrimitives(PrimitiveManager renderer, int yourDataLength)
        {
            //draw just the vertex data as points
			if (drawPoints)
				mDevice.DrawPrimitives(PrimitiveType.PointList, 0, mVertexData.Length);
        }


		public void RenderIndexedPrimitives(PrimitiveManager caller, int vbDataLength, int ibLength)
		{
			int numTriangles = NumberFaces;
			mDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, vertexPoints.Count, 0, numTriangles);
		}
		//    //Old method for rendering triangle strips
		//    //int stripIdx = 0;
		//    //foreach (List<EquilibriumPoint> strip in idxStrips)
		//    //{
		//    //    if (strip.Count >= 3)
		//    //        mDevice.DrawIndexedPrimitives(
		//    //            PrimitiveType.TriangleStrip, 0, 0, strip.Count,
		//    //            stripIdx * strip.Count, 
		//    //            strip.Count - 2);
		//    //    stripIdx++;
		//    //}
		//}
		[PGBrowsable]

		///Equal to the number of triangles.
		public int NumberFaces
		{
			get {
				if (vertexPoints == null)
					return 0;
				else return mSurfaceIndices.Length / 3;
				//old code for triangle strips
				//if (idxStrips == null)
				//	return 0;
				//else return idxStrips.Count * (idxStrips[0].Count - 2);
			}
		}

        public void InitializeMaterial()
        {
			mMaterial = new Material();
			mMaterial.Specular = Color.FromArgb(122,255,255,255);
            mMaterial.Ambient = Color.FromArgb(122,255,255,255);
            mMaterial.Diffuse = Color.FromArgb(122,255,255,255);
        }

        [PGBrowsable]
        public Material Material
        {
			get { return mMaterial; }
			set { mMaterial = value; }
        }

        public void ManagerDestroyed(PrimitiveManager caller)
        {
            //signals to registered primitiveWithVertices generator that renderer is or has been destroyed
            //so we should stop sending messages or at least relaying them to the renderer
            renderer = null;
        }

		public Array GetVertices()
        {
		    return (Array)mVertexData; //was SVector[]
        }
		public Array GetIndices() //was int[]
		{
			return (Array)mSurfaceIndices;
		}
		public int GetIndicesCount()
		{
			return mSurfaceIndices.Length;
		}
        public int GetVerticesCount()
        {
            if (mVertexData == null)
                return 0;
            else
                return mVertexData.Length;
        }

        [PGBrowsable]
		public Color MainVertexColor
		{
			get { return mVertexColor; }
			set
			{
				mVertexColor = value;
				if (!GlobalTimerEnabled)
				{
					Recreate();
				}
			}
		}

		public VertexFormats VertexFormat
		{
			get { return SVector.Format; }
		}

		protected float xMin = -1000;
        [PGBrowsable]
		public float XMin
		{
			get { return xMin; }
			set { xMin = value;
			if (!this.GlobalTimerEnabled)
				Recreate();
			}
		}
		protected float xMax = 1000;
        [PGBrowsable]
		public float XMax
		{
			get { return xMax; }
			set { xMax = value;
			if (!this.GlobalTimerEnabled)
				Recreate();
			}
		}
		protected int xCount = 50;
        [PGBrowsable]
		public int XCount
		{
			get { return xCount; }
			set { xCount = value;
			if (!this.GlobalTimerEnabled)
				Recreate();
			}
		}
		protected float yMin = -1000;
        [PGBrowsable]
		public float YMin
		{
			get { return yMin; }
			set { yMin = value;
			if (!this.GlobalTimerEnabled)
				Recreate();
			}
		}
		protected float yMax = 1000;
        [PGBrowsable]
		public float YMax
		{
			get { return yMax; }
			set { yMax = value;
			if (!this.GlobalTimerEnabled)
				Recreate();
			}
		}
		protected int yCount = 50;
        [PGBrowsable]
		public int YCount
		{
			get { return yCount; }
			set { yCount = value;
			if (!this.GlobalTimerEnabled)
				Recreate();
			}
		}
		protected float zMin = 1;
        [PGBrowsable]
		public float ZMin
		{
			get { return zMin; }
			set { zMin = value;
			if (!this.GlobalTimerEnabled)
				Recreate();
			}
		}
		protected float zMax =  100;
        [PGBrowsable]
		public float ZMax
		{
			get { return zMax; }
			set { zMax = value;
			if (!this.GlobalTimerEnabled)
				Recreate();
			}
		}
		protected int zCount = 1;
		protected int ZCount //NOT USED ANY MORE WE ONLY USE ONE LAYER FOR THE SURFACE.
		{
			get { return zCount; }
			//set
			//{
				//zCount = value;
				//if (!this.GlobalTimerEnabled)
				//	Recreate();
			//}
		}
        
		public void PostRender()
		{
			if (mGlobalTimerEnabled)
			{
				foreach (EquilibriumWave3d lsw in mWaves)
				{
					if (lsw.TimerEnabled)
					{
						lsw.DoMotion();
					}
				}
			}

		}
		protected bool mDoPreRender = true;
        [PGBrowsable]
		public bool PreRenderEnabled
		{
			get { return mDoPreRender; }
			set { mDoPreRender = value; }
		}
		protected bool mDoPostRender = true;
        [PGBrowsable]
		public bool PostRenderEnabled
		{
			get { return mDoPostRender; }
			set { mDoPostRender = value; }
		}
		protected bool mEnableCallRenderPrimitives = true;	 //if you want to see anything call this method
        
		//dont make PGBrowsable
		public bool EnableCallRenderPrimitives
		{
			get { return mEnableCallRenderPrimitives; }
			set { mEnableCallRenderPrimitives = value; }
		}
		public bool Visible
		{
			get { return mEnableCallRenderPrimitives; }
			set { EnableCallRenderPrimitives = true; }
		}
		protected bool mEnableCallRenderIndexedPrimitives = true;
        [PGBrowsable]
		public bool EnableCallRenderIndexedPrimitives
		{
			get { return mEnableCallRenderIndexedPrimitives; }
			set { mEnableCallRenderIndexedPrimitives = value; }
		}

		protected bool mGlobalTimerEnabled = false;
		[PGBrowsable]
		public bool GlobalTimerEnabled
		{
			get { return chkGlobalTimerEnabled.Checked; }
			set { 
				if (value != mGlobalTimerEnabled)
				{
					mGlobalTimerEnabled = value;
					chkGlobalTimerEnabled.Checked = value;
				}
			}
		}

		
		
		private void lbLateralWaves_SelectedIndexChanged(object sender, EventArgs e)
        {

			pgSelectedWaves.SelectedObjects = SelectedWaves();//lbLateralWaves.SelectedItems.OfType<object>().Select<.ToArray<object>();
        }
		private object[] SelectedWaves()
		{
			List<object> waveList = new List<object>();
			foreach(object o in lbLateralWaves.SelectedItems.OfType<object>())
			{
				waveList.Add((object)(EquilibriumWave3d)((ObjectListItem)o).ObjectAssociated);
			}
			return waveList.ToArray<object>();
		}

        private void btnAddWave_RandomNew_Click(object sender, EventArgs e)
        {
			DoAddWave(false, 0,0);
        }
		/// <summary>
		/// Mouse move event handler which provides direct interaction with the mouse and the form and the WaveEditor.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		void mFormForHandlers_MouseDown(object sender, MouseEventArgs e)
		{

			//throw new NotImplementedException();
			if (e.Button == MouseButtons.Right)
			{
				if (SelectMesh.OnlyObject.Selected == this)
				{
					Vector3 posWaveCenter = Mouse.OnlyMouse.SelectMesh_Intersection;
					DoAddWave(true, posWaveCenter.X, posWaveCenter.Y);
				}
				//else if (SelectMesh.OnlyObject.Visible == true)
				//{
				//    object lastSelected = SelectMesh.OnlyObject.Selected;
				//    SelectMesh.OnlyObject.Selected = this; //since the user already knows what is selcted we can change it and see if there is a hit under the mouse.
				//    if (SelectMesh.OnlyObject.
				//}
				//else SelectMesh.OnlyObject.Selected = this;
			}
		}

		private void DoAddWave(bool useCoordinates, float centerPositionX, float centerPositionY)
		{
			lbLateralWaves.DataSource = null;

			int maxWaveCount;
			if (useCoordinates)
				maxWaveCount = 1;
			else
				maxWaveCount = (int)nudCount.Value;

			for (int waveCount = 0; waveCount < maxWaveCount; waveCount++)
			{
				EquilibriumWave3d w = new EquilibriumWave3d(this.dTime);
				float cpx, cpy, cpz;
				cpz = (float)rnddec(nudZMin.Value, nudZMax.Value);   //this is just zero since the controls are not enabled

				if (!useCoordinates)
				{
					cpx = (float)rnddec(nudXMin.Value, nudXMax.Value);
					cpy = (float)rnddec(nudYMin.Value, nudYMax.Value);
					//set wave type based on control checked values
					if (rbWaveTypeCos.Checked)
						w.WaveType = EquilibriumWave3d.WaveEquationType.Cos;
					else if (rbWaveTypeNormalCurve.Checked)
						w.WaveType = EquilibriumWave3d.WaveEquationType.NormalCurve;
					else if (rbWaveTypeSin.Checked)
						w.WaveType = EquilibriumWave3d.WaveEquationType.Sin;
					else if (rbWaveTypeWater.Checked)
						w.WaveType = EquilibriumWave3d.WaveEquationType.WaterEffect;

			
				}
				else
				{
					cpx = centerPositionX;
					cpy = centerPositionY;
					w.WaveType = EquilibriumWave3d.WaveEquationType.NormalCurve;
				}

				Vector2 CP = new Vector2(cpx, cpy);
					//float vx = (float)rnddec(nudVXMin.Value, nudVXMax.Value);
					//float vy = (float)rnddec(nudVYMin.Value, nudVYMax.Value);
					//float vz = (float)rnddec(nudVZMin.Value, nudVZMax.Value);
					//Vector3 V = new Vector3(vx, vy, vz);

				w.CenterPosition = CP;
				w.MaxLifeTime = (float)rnddec(nudLifeTimeMin.Value, nudLifeTimeMax.Value);
				w.Cz = (float)rnddec(nudAmplitudeMin.Value, nudAmplitudeMax.Value);
				w.TimerEnabled = chkLocalTimerEnabled.Checked;

				//w.V = V;				
				w.T0 = (float)Time;
				decimal tempFreq = rnddec( nudFrequencyMin.Value, nudFrequencyMax.Value);
				w.Frequency = (float)tempFreq;


				w.FixedWaveCount = (float)nudFixedWaveCount.Value;

				w.Rho0 = (float)rnddec(nudR0Min.Value, nudR0Max.Value);
				w.RhoSpeed = (float)rnddec(nudRadialSpeedMin.Value, nudRadialSpeedMax.Value);
				w.LengthOfEffect = (float)rnddec(nudLengthOfEffectMin.Value, nudLengthOfEffectMax.Value);
				w.NormalVariance = (float)rnddec(nudNormalVarianceMin.Value, nudNormalVarianceMax.Value);
				w.OutOfBoundsZero = chkSetOutOfBoundsToZero.Checked;
				w.Frequency = (float)rnddec(nudFrequencyMin.Value, nudFrequencyMax.Value);

				//pick a direction
				if (cbRandomDirection.Checked)
				{
					//randomly pick only one direction
					int from1to3 = rnd.Next(1, 3);
					
					switch (from1to3)
					{
						case 1:
							w.UseXAxis = cbXDirection.Checked;
							break;
						case 2:
							w.UseYAxis = cbYDirection.Checked;
							break;
						case 3:
							w.UseZAxis = cbZDirection.Checked;
							break;
					}
				}
				else
				{
					w.UseXAxis = cbXDirection.Checked;
					w.UseYAxis = cbYDirection.Checked;
					w.UseZAxis = cbZDirection.Checked;
				}

				ObjectListItem listItem = new ObjectListItem(w, w.WaveNumber);
				listItem.ShortName = w.Name;
				mWaves.Add(w);
				mWaveListItems.Add(listItem);
				//lbLateralWaves.Focus();

				lbLateralWaves.Select();
			}

			lbLateralWaves.DataSource = mWaveListItems;
			lbLateralWaves.DisplayMember = "UniqueName";
			lbLateralWaves.ValueMember = "ObjectAssociated";
			lbLateralWaves.Update();

			Debug.WriteLine("t: " + Time.ToString() + ", wcount:" + mWaves.Count.ToString());

			Recreate();
		}

        private void btnRemoveSelectedWaves_Click(object sender, EventArgs e)
        {
			List<ObjectListItem> itemsToRemove = lbLateralWaves.SelectedItems.OfType<ObjectListItem>().ToList<ObjectListItem>();

            lbLateralWaves.DataSource = null;
            foreach (object o in itemsToRemove)
            {
                //change lbLateralWaves.DataSource			
				ObjectListItem listItem = ((ObjectListItem)o);
				mWaves.Remove((EquilibriumWave3d)listItem.ObjectAssociated);
				WaveListItems.Remove(listItem);
            }
			lbLateralWaves.DataSource = WaveListItems;
			lbLateralWaves.DisplayMember = "UniqueName";
            lbLateralWaves.Update();
			Recreate();
        }

		private void rbWaveTypeNormalCurve_CheckedChanged(object sender, EventArgs e)
		{
			nudNormalVarianceMin.Enabled = rbWaveTypeNormalCurve.Checked;
		}

		private void Fluid_FormClosing(object sender, FormClosingEventArgs e)
		{
			if (e.CloseReason == CloseReason.UserClosing)
				e.Cancel = true;
			this.Hide();
		}

		private void chkGlobalTimerEnabled_CheckedChanged(object sender, EventArgs e)
		{
			this.GlobalTimerEnabled = chkGlobalTimerEnabled.Checked;
		}

		private void nudGlobalTime_ValueChanged(object sender, EventArgs e)
		{
			if (!initializing)
			{
				this.Time = (double)nudGlobalTime.Value;
			}
		}

		private void btnEnableAllTimers_Click(object sender, EventArgs e)
		{
			SetEnabledForAll(true);
			this.WindowState = FormWindowState.Minimized;
			btnEnableAllTimers.ForeColor = Color.Black;
			btnDisableAllTimers.ForeColor = Color.Green;
		}
		private void SetEnabledForAll(bool enabled)
		{
			foreach (object o in lbLateralWaves.Items)
			{
				EquilibriumWave3d w = (EquilibriumWave3d)((ObjectListItem)o).ObjectAssociated;
				w.TimerEnabled = enabled;
			}
			//last but not least enable the global timer
			this.GlobalTimerEnabled = enabled;
		}

		private void btnDisableAllTimers_Click(object sender, EventArgs e)
		{
			SetEnabledForAll(false);
			btnEnableAllTimers.ForeColor = Color.Green;
			btnDisableAllTimers.ForeColor = Color.Black;

		}


		private RadioButton oldColored = null;
		private void SetDefaultOptions0(EquilibriumWave3d.WaveEquationType newWaveType, object sender)
		{
			//if (oldColored != null)
			//	oldColored.ForeColor = Color.Black;

			//oldColored = (RadioButton)sender; 
			//oldColored.ForeColor = Color.Green;

			switch (newWaveType)
			{
				case EquilibriumWave3d.WaveEquationType.WaterEffect:
					nudAmplitudeMax.Value = 2;
					nudAmplitudeMin.Value = .25M;
					nudFrequencyMin.Value = .1M;
					nudFrequencyMax.Value = 5.0M;
					break;
				case EquilibriumWave3d.WaveEquationType.Sin://fall through
				case EquilibriumWave3d.WaveEquationType.Cos:
					nudAmplitudeMin.Value = (decimal).51;
					nudAmplitudeMax.Value = 3;
					nudFrequencyMin.Value = 1;
					nudFrequencyMax.Value = 5;
					break;
				case EquilibriumWave3d.WaveEquationType.NormalCurve:
					decimal radialLength = 10;
					nudLengthOfEffectMin.Value = radialLength;
					nudLengthOfEffectMax.Value = radialLength;
					nudAmplitudeMax.Value = 10;		//hey even 5 foot waves are good
					nudAmplitudeMin.Value = 50;		//wow a fifty-foot wave
					nudR0Min.Value = radialLength;
					nudXMax.Value = ((decimal)xMax);// - radialLength);
					nudXMin.Value = ((decimal)xMin);// - radialLength);
					nudYMin.Value = (decimal)yMin;
					nudYMax.Value = (decimal)yMax;
					nudRadialSpeedMin.Value = 10;
					nudRadialSpeedMax.Value = 50;
					break;
			}
		}

		private void pgSelfViewer_PropertyValueChanged(object s, PropertyValueChangedEventArgs e)
		{
			Recreate();
		}

		private void pgSelectedWaves_PropertyValueChanged(object s, PropertyValueChangedEventArgs e)
		{
			Recreate();
		}

		//private void chkChangeCamera_Click(object sender, EventArgs epsilon)
		//{
		//    onlyBuoy.AllowChangeCamera = chkChangeCamera.Checked;
		//}

		private void tcNewWave_SelectedIndexChanged(object sender, EventArgs e)
		{
			//if (tcNewWave.SelectedTab == tpSinAndCos)
			//	rbWaveTypeCos.Checked = true;
			//else 
			if (tcNewWave.SelectedTab == tpNormal)
				rbWaveTypeNormalCurve.Checked = true;
		}

		private void nudXMin_ValueChanged(object sender, EventArgs e)
		{
			if (!initializing)
			{
				if (nudXMin.Value > nudXMax.Value)
					nudXMax.Value = nudXMin.Value;
			}
		}

		private void nudYMin_ValueChanged(object sender, EventArgs e)
		{
			if (!initializing)
			{
				if (nudYMin.Value > nudYMax.Value)
					nudYMax.Value = nudYMin.Value;
			}
		}

		private void nudZMin_ValueChanged(object sender, EventArgs e)
		{
			if (!initializing)
			{
				if (nudZMin.Value > nudZMax.Value)
					nudZMax.Value = nudZMin.Value;
			}
		}

		private void nudXMax_ValueChanged(object sender, EventArgs e)
		{
			if (!initializing)
			{
				if (nudXMax.Value < nudXMin.Value)
					nudXMin.Value = nudXMax.Value;
			}
		}

		private void nudYMax_ValueChanged(object sender, EventArgs e)
		{
			if (!initializing)
			{
				if (nudYMax.Value < nudYMin.Value)
					nudYMin.Value = nudYMax.Value;
			}
		}

		private void nudZMax_ValueChanged(object sender, EventArgs e)
		{
			if (!initializing)
			{
				if (nudZMax.Value < nudZMin.Value)
					nudZMin.Value = nudZMax.Value;
			}
		}

		private void btnDefaults_Click(object sender, EventArgs e)
		{

			SetDefaultsOfSelectedType(sender);

		}

		private void SetDefaultsOfSelectedType(object sender)
		{
			EquilibriumWave3d.WaveEquationType type = EquilibriumWave3d.WaveEquationType.NormalCurve;

			//((RadioButton)sender).ForeColor = Color.Crimson;

			if (rbWaveTypeCos.Checked)
				type = EquilibriumWave3d.WaveEquationType.Cos;

			else if (rbWaveTypeNormalCurve.Checked)
				type = EquilibriumWave3d.WaveEquationType.NormalCurve;

			else if (rbWaveTypeSin.Checked)
				type = EquilibriumWave3d.WaveEquationType.Sin;

			else if (rbWaveTypeWater.Checked)
				type = EquilibriumWave3d.WaveEquationType.WaterEffect;

			SetDefaultOptions0(type, sender);
		}

		//private void rbWaveType_CheckedChanged(object sender, EventArgs e)
		//{
		//    if (cbAutoDefaults.CheckState == CheckState.Checked)
		//        SetDefaultsOfSelectedType(sender);
		//}

	
    }

}
